

package helper

import (
	"fmt"
	jsoniter "github.com/json-iterator/go"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
)

// EnableCrossDomain sets the `Access-Control-Allow-Methods` header and the
// `Access-Control-Allow-Origin` header to the response to enable cross domain.
//
// TODO: We should restrict the origin, and may set in `config.toml`.
func EnableCrossDomain(w http.ResponseWriter, r *http.Request, whiteList... string) {
	origin := r.Header.Get("Origin")
	allowHeaders := "Auth-Token,Content-Type"

	if len(whiteList) == 0 {
		allowHeaders += ",Access-Token"
	} else {
		flag := false
		for i := range whiteList {
			if whiteList[i] == origin || whiteList[i] == "*" {
				allowHeaders += ",Access-Token"
				flag = true
				break
			}
		}

		if !flag {
			origin = ""
			w.Header().Del("Access-Control-Allow-Origin")
			return
		}
	}


	if strings.HasPrefix(r.RequestURI, "/Upload/Texture") {
		origin = "*"
	}
	if origin == "" { // not cross origin
		return
	}

	header := w.Header()
	header.Set("Access-Control-Allow-Methods", "OPTIONS,POST,GET")
	header.Set("Access-Control-Allow-Origin", origin)
	header.Set("Access-Control-Allow-Headers",allowHeaders)
}

type requestConfig struct {
	Method string
	Headers map[string]string
	Body io.Reader
}

func (r *requestConfig) checkProperty() {
	if r.Method == "" {
		r.Method = "GET"
	}
}

func NewRequestConfig() *requestConfig {
	return &requestConfig{
		Method: "GET",
		Headers: make(map[string]string),
	}
}

// Request http 请求
func Request(url string, config ...map[string]interface{}) ([]byte, error) {
	requestConfig := NewRequestConfig()

	if len(config) > 0 {
		cnf := config[0]
		body, ok := cnf["body"]
		if ok && body != nil {
			cnf["body"] = nil
		}
		bytes, err := ToJSON(cnf)
		if err != nil {
			return nil, err
		}
		if err := FromJSON(bytes, requestConfig); err != nil {
			return nil, err
		}
		if body != nil{
			if b, ok := body.(io.Reader); ok {
				requestConfig.Body = b
			}
		}
	}

	request, err := http.NewRequest(requestConfig.Method, url, requestConfig.Body)
	if err != nil {
		return nil, err
	}

	// 设置 header
	for k, v := range requestConfig.Headers {
		request.Header.Set(k, v)
	}

	resp, err := http.DefaultClient.Do(request)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("http status: %v , result: %v", resp.StatusCode, string(respBody))
	}
	return respBody, err
}

// Get create a get request to the server.
func Get(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("http status: %v %v", resp.StatusCode, resp.Status)
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// Post create a post request to the server.
func Post(url string, data url.Values) ([]byte, error) {
	resp, err := http.PostForm(url, data)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("http status: %v %v", resp.StatusCode, resp.Status)
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// 以请求体的方式请求
// 第二个参数是 Content-Type，第三个参数是 io.Reader interface
// strings.NewReader	byte.NewReader	bytes.NewBuffer 实现了 read 方法
func PostBody(url string, data interface{}) ([]byte, error) {
	b, err := jsoniter.MarshalToString(data)
	if err != nil {
		return nil, err
	}
	fmt.Printf("param --> %v\n", b)
	resp, err := http.Post(url, "application/json", strings.NewReader(b))
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("http status: %v %v", resp.StatusCode, resp.Status)
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// Write write a string response to the web client.
func Write(w http.ResponseWriter, args ...interface{}) (int, error) {
	header := w.Header()

	header.Set("Content-Type", "text/plain")
	header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
	header.Set("Pragma", "no-cache")
	header.Set("Expires", "0")

	return w.Write([]byte(fmt.Sprint(args...)))
}

// Writef write a string response to the web client with format string.
func Writef(w http.ResponseWriter, format string, args ...interface{}) (int, error) {
	header := w.Header()

	header.Set("Content-Type", "text/plain")
	header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
	header.Set("Pragma", "no-cache")
	header.Set("Expires", "0")

	return w.Write([]byte(fmt.Sprint(args...)))
}

// WriteJSON write a json response to the web client.
func WriteJSON(w http.ResponseWriter, obj interface{}) (int, error) {
	header := w.Header()

	header.Set("Content-Type", "application/json")
	header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
	header.Set("Pragma", "no-cache")
	header.Set("Expires", "0")

	bytes, err := ToJSON(obj)
	if err != nil {
		return 0, err
	}

	return w.Write(bytes)
}

// WriteFile write a file stream to the webbrowser.
func WriteFile(w http.ResponseWriter, path, name string) (int, error) {
	stat, err := os.Stat(path)
	if os.IsNotExist(err) {
		w.WriteHeader(http.StatusNotFound)
		return 0, err
	}

	if stat.IsDir() {
		w.WriteHeader(http.StatusNotFound)
		return 0, fmt.Errorf("%v is not a file", path)
	}

	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		return 0, err
	}

	header := w.Header()

	header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%v"`, url.QueryEscape(name)))
	header.Set("Content-Length", strconv.Itoa(len(bytes)))
	header.Set("Content-Type", "application/octet-stream; charset=GB2312")

	return w.Write(bytes)
}
